-- Persional Settings.
local sellWhite = false

-- global function >>
local esLink = ""
local equipLoc = ""
local loc
local loc2
local bag = 0
local slot = 0
local nSlot = 0
local shdCnt = 0
local FreeLoc = {}
local ShardLoc = {}
local bagList = {}
local StackLoc = {}

local splitTime
local itemCount
local itemLink
local itemStackCount

local function Print(msg)
    if msg == nil then return end

    if( DEFAULT_CHAT_FRAME ) then
        DEFAULT_CHAT_FRAME:AddMessage(msg);
    end

end

local function Pack(...)
    local t = {}
    local item
    if select('#', ...) > 0 then
        for i = 1, select('#', ...) do
            item = select(i, ...)
            esLink,_ = string.find(item, "Hitem")
            if esLink then
                _,_,item = string.find(item, "^.*%[(.*)%].*$")
            end
            if item ~= "" then
                table.insert(t, item)
            end
        end
    end
    return t
end

local function split(msg, sp)
    if msg == nil then
        msg = ""
    end
    i,j = string.find(msg, sp)
    if i then
        return string.sub(msg, 1, i-1), split(string.sub(msg, j+1, -1), sp)
    else
        return msg
    end
end

local function Chk(bagId,slotId)
	esLink = GetContainerItemLink(bagId,slotId)
	if esLink then
		_,_,esLink = string.find(esLink, "%:(%d+)")

		if tonumber(esLink) == EnoughShards_SoulShardID then
			return true
		end
	end
	return false
end

local function Del(bagId,slotId)
    PutItemInBackpack()
    if CursorHasItem() then
        return false
    end
    PickupContainerItem(bagId,slotId)
    DeleteCursorItem()
    return true
end

local function GetFreeLoc()
    shdCnt = 0

    if next(EnoughShardsSave.Profile.ChkInfo["bag"]) then
        for _, bag in ipairs(EnoughShardsSave.Profile.ChkInfo["bag"]) do
            for slot = GetContainerNumSlots(bag),1,-1 do
                if shdCnt >= ShardLoc["Cnt"] or shdCnt >= EnoughShardsSave.Profile.ChkInfo["count"] then
                    break
                end

                esLink = GetContainerItemLink(bag,slot)
                if not esLink then
                    shdCnt = shdCnt + 1
                    if not FreeLoc[shdCnt] then
                        FreeLoc[shdCnt]= {}
                    end
                    FreeLoc[shdCnt]["bag"] = bag
                    FreeLoc[shdCnt]["slot"] = slot
                    FreeLoc[shdCnt]["used"] = false
                elseif Chk(bag,slot) then
                    shdCnt = shdCnt + 1
                    if not FreeLoc[shdCnt] then
                        FreeLoc[shdCnt]= {}
                    end
                    FreeLoc[shdCnt]["bag"] = bag
                    FreeLoc[shdCnt]["slot"] = slot
                    FreeLoc[shdCnt]["used"] = true
                end
            end
        end
    else
        for bag = NUM_BAG_FRAMES,0,-1 do
            for slot = GetContainerNumSlots(bag),1,-1 do
                if shdCnt >= ShardLoc["Cnt"] or shdCnt >= EnoughShardsSave.Profile.ChkInfo["count"] then
                    break
                end

                esLink = GetContainerItemLink(bag,slot)
                if not esLink then
                    shdCnt = shdCnt + 1
                    if not FreeLoc[shdCnt] then
                        FreeLoc[shdCnt]= {}
                    end
                    FreeLoc[shdCnt]["bag"] = bag
                    FreeLoc[shdCnt]["slot"] = slot
                    FreeLoc[shdCnt]["used"] = false
                elseif Chk(bag,slot) then
                    shdCnt = shdCnt + 1
                    if not FreeLoc[shdCnt] then
                        FreeLoc[shdCnt]= {}
                    end
                    FreeLoc[shdCnt]["bag"] = bag
                    FreeLoc[shdCnt]["slot"] = slot
                    FreeLoc[shdCnt]["used"] = true
                end
            end
        end
    end
    FreeLoc["Cnt"] = shdCnt
    shdCnt = shdCnt + 1
    FreeLoc[shdCnt] = nil
end

local function GetShardLoc()
    shdCnt = 0

    for bag = NUM_BAG_FRAMES,0,-1 do
        bagList[bag] = false
    end

    if next(EnoughShardsSave.Profile.ChkInfo["bag"]) then
        for _, bag in ipairs(EnoughShardsSave.Profile.ChkInfo["bag"]) do
            if not bagList[bag] then
                bagList[bag] = true
                for slot = GetContainerNumSlots(bag),1,-1 do
                    if Chk(bag,slot) then
                        shdCnt = shdCnt + 1
                        if not ShardLoc[shdCnt] then
                            ShardLoc[shdCnt]= {}
                        end
                        ShardLoc[shdCnt]["bag"] = bag
                        ShardLoc[shdCnt]["slot"] = slot
                        ShardLoc[shdCnt]["needmove"] = true
                    end
                end
            end
        end
    end

    for bag = NUM_BAG_FRAMES,0,-1 do
        if not bagList[bag] then
            bagList[bag] = true
            for slot = GetContainerNumSlots(bag),1,-1 do
                if Chk(bag,slot) then
                    shdCnt = shdCnt + 1
                    if not ShardLoc[shdCnt] then
                        ShardLoc[shdCnt]= {}
                    end
                    ShardLoc[shdCnt]["bag"] = bag
                    ShardLoc[shdCnt]["slot"] = slot
                    ShardLoc[shdCnt]["needmove"] = true
                end
            end
        end
    end

    ShardLoc["Cnt"] = shdCnt
    shdCnt = shdCnt + 1
    ShardLoc[shdCnt] = nil
end

local function CheckNotMove(bag,slot)
    for _, loc in ipairs(FreeLoc) do
        if loc["bag"] == bag and loc["slot"] == slot then
            return true
        end
    end
    return false
end

local function CheckLoc()
    for _, loc2 in ipairs(ShardLoc) do
        if CheckNotMove(loc2["bag"],loc2["slot"]) then
            loc2["needmove"] = false
        end
    end
end

local function GetFree()
    for _, loc in ipairs(FreeLoc) do
        if not loc["used"] then
            loc["used"] = true
            return loc
        end
    end
    return nil
end

local function Move(oBag, oSlot, nBag, nSlot)
    if CursorHasItem() then
        PutItemInBackpack()
    end
    PickupContainerItem(oBag,oSlot)
    PickupContainerItem(nBag,nSlot)
end

local function Remain()
	if EnoughShards.SplitNum or EnoughShards.StackCount then
		return
	end

    -- Count Shard
    GetShardLoc()

    -- Count Free Space
    GetFreeLoc()

    -- Check location
    CheckLoc()

    -- Move item
    for shdCnt,loc in ipairs(ShardLoc) do
        if shdCnt > FreeLoc["Cnt"] or shdCnt > EnoughShardsSave.Profile.ChkInfo["count"] then
            Del(loc["bag"],loc["slot"])
        else
            if loc["needmove"] then
                loc2 = GetFree()
                if loc2 then
                    Move(loc["bag"],loc["slot"],loc2["bag"],loc2["slot"])
                end
            end
        end
    end
end

local function SellPoor()
    for bag = NUM_BAG_FRAMES,0,-1 do
        for slot = GetContainerNumSlots(bag),1,-1 do
            esLink = GetContainerItemLink(bag,slot)
            if esLink then
                _,_,itemLink = string.find(esLink, "^.*%[(.*)%].*$")
                _, _, esLink, _, _, _, _, _,equipLoc = GetItemInfo(esLink)

                if not EnoughShardsSave.NotSell[itemLink] and (EnoughShardsSave.WantSell[itemLink] or (esLink == 0 ) or (sellWhite and esLink == 1 and equipLoc ~= "") ) then
                    UseContainerItem(bag,slot)
                end
            end
        end
    end
end

--- Auto Split Function
local function GetFreeLocForSplit(Cnt)
    shdCnt = 0

	for bag = NUM_BAG_FRAMES,0,-1 do
		for slot = GetContainerNumSlots(bag),1,-1 do
			if shdCnt >= Cnt then
				break
			end

			esLink = GetContainerItemLink(bag,slot)
			if not esLink then
				shdCnt = shdCnt + 1
				if not FreeLoc[shdCnt] then
					FreeLoc[shdCnt]= {}
				end
				FreeLoc[shdCnt]["bag"] = bag
				FreeLoc[shdCnt]["slot"] = slot
				FreeLoc[shdCnt]["used"] = false
			end
		end
	end

    FreeLoc["Cnt"] = shdCnt
    shdCnt = shdCnt + 1
    FreeLoc[shdCnt] = nil
end

local function SplitItem(bag, slot, num)
    _, itemCount = GetContainerItemInfo(bag, slot)

    if itemCount > num and num > 0 then
        GetFreeLocForSplit(math.ceil(itemCount/num))

		-- Start Split
		loc2 = GetFree()
		if loc2 then
			splitTime = GetTime()

			EnoughShards.SplitBag = bag
			EnoughShards.SplitSlot = slot
			EnoughShards.SplitNum = num

			SplitContainerItem(bag, slot, num)
			PickupContainerItem(loc2["bag"], loc2["slot"])

			EnoughShardsProgramLoadFrame:Show()
		end
    end
end

local function StackSplitAllButton_OnClick(self)
	local item = StackSplitFrame.owner

    StackSplitFrame:Hide()

    if not (EnoughShards.SplitNum or EnoughShards.StackCount) and item then
        SplitItem(item:GetParent():GetID(), item:GetID(), StackSplitFrame.split)
    end
end

-- Auto Stack Item
local function SortStack()
	local chg = false

	for i, v in ipairs(StackLoc) do
		if StackLoc[i+1] then
			if StackLoc[i]["cnt"] < StackLoc[i+1]["cnt"] then
				chg = true
				StackLoc[i+1], StackLoc[i] = StackLoc[i], StackLoc[i+1]
			end
		end
	end

	if chg then
		return SortStack()
	end
end

local function GetLocForStack(itemLink, itemStackCount)
    shdCnt = 0
	EnoughShards.StackSum = 0

	for bag = NUM_BAG_FRAMES,0,-1 do
		for slot = GetContainerNumSlots(bag),1,-1 do

			esLink = GetContainerItemLink(bag,slot)
			if esLink then
				_,_,esLink = string.find(esLink, "%:(%d+)")

				if esLink == EnoughShards.StackItemID then
					_, itemCount = GetContainerItemInfo(bag, slot)
					if itemCount < itemStackCount then
						shdCnt = shdCnt + 1
						if not StackLoc[shdCnt] then
							StackLoc[shdCnt]= {}
						end
						StackLoc[shdCnt]["bag"] = bag
						StackLoc[shdCnt]["slot"] = slot
						StackLoc[shdCnt]["cnt"] = itemCount

						EnoughShards.StackSum = EnoughShards.StackSum + itemCount
					end
				end
			end
		end
	end

    StackLoc["Cnt"] = shdCnt
    shdCnt = shdCnt + 1
    StackLoc[shdCnt] = nil

	-- Sort
	SortStack()

	-- Calc
	EnoughShards.StackCell = math.ceil(EnoughShards.StackSum / EnoughShards.StackCount)
end

local function StackItemOnce()
	local start, last, chg = 1, StackLoc["Cnt"], false

	-- ReCount
	for i, v in ipairs(StackLoc) do
		esLink = GetContainerItemLink(v["bag"], v["slot"])
		if esLink then
			_, v["cnt"] = GetContainerItemInfo(v["bag"], v["slot"])
		else
			v["cnt"] = 0
		end
	end

	-- Stack main
	while start < last do
		if StackLoc[start]["cnt"] < EnoughShards.StackCount then
			if StackLoc[last]["cnt"] > 0 then
				if StackLoc[last]["cnt"] + StackLoc[start]["cnt"] <= EnoughShards.StackCount then
					PickupContainerItem(StackLoc[last]["bag"], StackLoc[last]["slot"])
					PickupContainerItem(StackLoc[start]["bag"], StackLoc[start]["slot"])
				else
					SplitContainerItem(StackLoc[last]["bag"], StackLoc[last]["slot"], EnoughShards.StackCount - StackLoc[start]["cnt"])
					PickupContainerItem(StackLoc[start]["bag"], StackLoc[start]["slot"])
				end
				chg = true
				start = start + 1
				last = last - 1
			else
				last = last - 1
			end
		else
			start = start + 1
		end
	end

	if not chg then
		EnoughShards.StackCount = nil
		EnoughShards.StackCell = nil
		EnoughShards.StackSum = nil
		EnoughShards.StackItemID = nil
	end
end

local function StackItem(itemLink, itemStackCount)
	GetLocForStack(itemLink, itemStackCount)

	if EnoughShards.StackCell < StackLoc["Cnt"] then
		splitTime = GetTime()
		StackItemOnce()
		EnoughShardsProgramLoadFrame:Show()
	else
		EnoughShards.StackCount = nil
		EnoughShards.StackCell = nil
		EnoughShards.StackSum = nil
		EnoughShards.StackItemID = nil
	end
end

local function AltClkItem(self, button)
	if EnoughShards.SplitNum or EnoughShards.StackCount then
		return
	end

	if IsAltKeyDown() then
		itemLink = GetContainerItemLink(self:GetParent():GetID(), self:GetID())
		if itemLink then
			_, _, _, _, _, _, _, itemStackCount = GetItemInfo(itemLink)
			if itemStackCount > 1 then
				_,_,esLink = string.find(itemLink, "%:(%d+)")
				EnoughShards.StackItemID = esLink
				EnoughShards.StackCount = itemStackCount
				StackItem(itemLink, itemStackCount)
			end
		end
	end
end
-- global function <<

-- Init function >>
function EnoughShards.Event.OnLoad()
    local ev

    --Print(EnoughShards_ONLOAD_MESSAGE1)
    --Print(EnoughShards_ONLOAD_MESSAGE2)

    -- Hook into the addon itself to prevent garbage collection
    this.EnoughShards = EnoughShards;

    SLASH_EnoughShards1 = '/ensd'
    SlashCmdList['EnoughShards'] = EnoughShards.Event.OnSlashCmd

    for ev in pairs(EnoughShards_EVENT) do
        this:RegisterEvent(EnoughShards_EVENT[ev]);
    end
end
-- Init funtion <<

-- Event function >>
function EnoughShards.Event.OnEvent(_event, ...)

    if EnoughShards.Event[_event] then
        EnoughShards.Event[_event](_event, ...)
    else
        Print("event ".._event.." is triggered.")
        local arg1,arg2,arg3,arg4 = ...
        arg1 = arg1 or ""
        arg2 = arg2 or ""
        arg3 = arg3 or ""
        arg4 = arg4 or ""
        Print("event arg is "..arg1..","..arg2..","..arg3..","..arg4..";")
    end
end

function EnoughShards.Event.VARIABLES_LOADED(event, ...)
    if not EnoughShardsSave or not EnoughShardsSave.Profile or not EnoughShardsSave.Profile.ChkInfo then
        EnoughShardsSave = {}
        EnoughShardsSave.Profile = {
            Stoped = false,
            ChkInfo = {
                ["count"] = EnoughShards_MAX,
                ["bag"] = {},
            }
        }
    end
    if not EnoughShardsSave.NotSell then
        EnoughShardsSave.NotSell = {}
    end
    if not EnoughShardsSave.WantSell then
        EnoughShardsSave.WantSell = {}
    end
	if not EnoughShardsSave.WaitTime then
		EnoughShardsSave.WaitTime = 1
	end

	-- Hook StackSplitFrame
	StackSplitOkayButton:SetWidth(40)
	StackSplitOkayButton:SetPoint("RIGHT", StackSplitFrame, "BOTTOM", -23, 32)
	StackSplitCancelButton:SetWidth(40)
	StackSplitCancelButton:SetPoint("LEFT", StackSplitFrame, "BOTTOM", 23, 32)

	StackSplitAllButton = StackSplitAllButton or CreateFrame("Button", "StackSplitAllButton", StackSplitFrame, "UIPanelButtonTemplate")
	StackSplitAllButton:SetWidth(40)
	StackSplitAllButton:SetHeight(24)
	StackSplitAllButton:SetPoint("CENTER", StackSplitFrame, "BOTTOM", 0, 32)
	StackSplitAllButton:SetText(EnoughShards_TXT_AUTO)
	StackSplitAllButton:SetScript("OnClick", StackSplitAllButton_OnClick)

	-- Stack item
	hooksecurefunc("ContainerFrameItemButton_OnModifiedClick", AltClkItem)
end

function EnoughShards.Event.UNIT_SPELLCAST_SUCCEEDED(event, ...)
    if EnoughShardsSave.Profile.Stoped then return end
    Remain()
end

function EnoughShards.Event.PLAYER_REGEN_ENABLED(event, ...)
    if EnoughShardsSave.Profile.Stoped then return end
    Remain()
end

function EnoughShards.Event.MERCHANT_SHOW(event, ...)
	--SellPoor()
end
-- Event function <<

-- Slash function >>
function EnoughShards.Event.OnSlashCmd(msg)
    if not msg then
        return
    end

    for cmd,args in string.gmatch(msg,"(%a+) *(.+)") do
        cmd = string.lower(cmd)

        if EnoughShards.SlashCmd[cmd] then
            EnoughShards.SlashCmd[cmd](args)
            return
        end
    end

    for cmd,args in string.gmatch(msg.." test","(%a+) *(.+)") do
        cmd = string.lower(cmd)

        if EnoughShards.SlashCmd[cmd] then
            EnoughShards.SlashCmd[cmd](args)
            return
        end
    end

    Print("==========EnoughShards==========")

    if next(EnoughShardsSave.Profile.ChkInfo["bag"]) then
        esLink = ""
        for _,i in ipairs(EnoughShardsSave.Profile.ChkInfo["bag"]) do
            if esLink ~= "" then
                esLink = esLink..","..i
            else
                esLink = esLink..i
            end
        end
        Print(EnoughShards_MSG_KEEPBAG1..esLink..EnoughShards_MSG_KEEPBAG2)
    end

    if EnoughShardsSave.Profile.ChkInfo["count"] < EnoughShards_MAX then
        Print(EnoughShards_MSG_KEEPSHD1..EnoughShardsSave.Profile.ChkInfo["count"]..EnoughShards_MSG_KEEPSHD2)
    end

    if next(EnoughShardsSave.NotSell) then
        Print(EnoughShards_MSG_KEEPITEM)
        esLink = ""
        for i in pairs(EnoughShardsSave.NotSell) do
            if esLink ~= "" then
                esLink = esLink..",  "..i
            else
                esLink = i
            end
        end
        Print(esLink)
    end
    if next(EnoughShardsSave.WantSell) then
        Print(EnoughShards_MSG_SELLITEM)
        esLink = ""
        for i in pairs(EnoughShardsSave.WantSell) do
            if esLink ~= "" then
                esLink = esLink..",  "..i
            else
                esLink = i
            end
        end
        Print(esLink)
    end
    Print(EnoughShards_MSG_Help)
    Print("=================================")
end

function EnoughShards.SlashCmd.help(args)
    Print("==========EnoughShards==========")
    for cmd in pairs(EnoughShards.SlashCmd) do
        if cmd ~= "help" then
            EnoughShards.SlashCmd[cmd]("")
        end
    end
    Print("=================================")
end

function EnoughShards.SlashCmd.es(args)
    local arg1 = args

    if arg1 == "stop" then
        Print(EnoughShards.MyAddOnsData.name.." "..EnoughShards.MyAddOnsData.version..EnoughShards_MSG_STOP)
        EnoughShardsSave.Profile.Stoped = true
        return
    elseif arg1 == "start" then
        if EnoughShardsSave.Profile.Stoped then
            Print(EnoughShards.MyAddOnsData.name.." "..EnoughShards.MyAddOnsData.version..EnoughShards_MSG_START)
            EnoughShardsSave.Profile.Stoped = false
        end
        return
    end

    Print("/ensd es start|stop　"..EnoughShards_MSG_Help_es)
    return
end

function EnoughShards.SlashCmd.keep(args)

    if args == "all" then
        EnoughShardsSave.WantSell = {}
        return
    elseif args ~= "test" and args ~= "" then
        local t = Pack(split(args, ","))

        if next(t) then
            local arg1 = tonumber(t[1])

            if arg1 and arg1 >= 0 then
                EnoughShardsSave.Profile.ChkInfo["count"] = EnoughShards_MAX
                EnoughShardsSave.Profile.ChkInfo["bag"] = {}

                for _,i in ipairs(t) do
                    arg1 = tonumber(i)
                    if arg1 and arg1 >= 0 then
                        if arg1 > NUM_BAG_FRAMES then
                            EnoughShardsSave.Profile.ChkInfo["count"] = arg1
                        else
                            esLink = false
                            for _,j in ipairs(EnoughShardsSave.Profile.ChkInfo) do
                                if arg1 == j then
                                    esLink = true
                                    break
                                end
                            end
                            if not esLink then
                                table.insert(EnoughShardsSave.Profile.ChkInfo["bag"], arg1)
                            end
                        end
                    end
                end
                Remain()
            else
                for _,i in ipairs(t) do
                    if EnoughShardsSave.WantSell[i] then
                        EnoughShardsSave.WantSell[i] = nil
                    else
                        EnoughShardsSave.NotSell[i] = true
                    end
                end
            end
            return
        end
    end

    Print("/ensd keep ? "..EnoughShards_MSG_Help_keep)
    return
end

function EnoughShards.SlashCmd.notkeep(args)
    if args == "all" then
        EnoughShardsSave.NotSell = {}
        return
    elseif args ~= "test" and args ~= "" then
        local t = Pack(split(args, ","))

        if next(t) then
            for _,i in ipairs(t) do
                if EnoughShardsSave.NotSell[i] then
                    EnoughShardsSave.NotSell[i] = nil
                else
                    EnoughShardsSave.WantSell[i] = true
                end
            end
            return
        end
    end

    Print("/ensd notkeep ? "..EnoughShards_MSG_Help_notkeep)
    return
end

function EnoughShards.SlashCmd.wait(args)
	local arg1 = args

	if arg1 ~= "" then
		arg1 = tonumber(arg1)

		if not arg1 or arg1 <= 0 then arg1 = 1 end

		EnoughShardsSave.WaitTime = arg1
		return
	end
	Print("/ensd wait ? "..EnoughShards_MSG_Help_wait)
    return
end
-- Slash function <<

-- Update function >>
function EnoughShards.Event.OnUpdate()
	if not (EnoughShards.SplitNum or EnoughShards.StackCount) then
		EnoughShardsProgramLoadFrame:Hide()
		return
	end

	if GetTime() - splitTime < EnoughShardsSave.WaitTime then
		return
	end

	splitTime = GetTime()

	if EnoughShards.SplitNum then
		-- Split Item
		_, itemCount = GetContainerItemInfo(EnoughShards.SplitBag, EnoughShards.SplitSlot)

		if itemCount > EnoughShards.SplitNum then
			loc2 = GetFree()
			if loc2 then
				SplitContainerItem(EnoughShards.SplitBag, EnoughShards.SplitSlot, EnoughShards.SplitNum)
				PickupContainerItem(loc2["bag"], loc2["slot"])
			else
				EnoughShards.SplitBag = nil
				EnoughShards.SplitSlot = nil
				EnoughShards.SplitNum = nil
			end
		else
			EnoughShards.SplitBag = nil
			EnoughShards.SplitSlot = nil
			EnoughShards.SplitNum = nil
		end
	elseif EnoughShards.StackCount then
		StackItemOnce()
	end
end
-- Update function <<